using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using Northwoods.Go;
using Workflows.Components.Rules;

namespace Rules.Diagram
{
    /// <summary>
    /// drag-drop node on the diagram
    /// </summary>
    [Serializable]
    public class RuleNode : GoTextNode, IHighlit
    {
        /// <summary>
        /// 
        /// </summary>
        public RuleNode()
        {
            InitCommon();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="kind"></param>
        public RuleNode(RuleNodeType kind)
        {
            InitCommon();
            this.Kind = kind;
            ChangeNodeForKind(kind);
            ChangeInfoForKind(kind);
        }

        private void InitCommon()
        {
            this.Shadowed = true;
            this.Resizable = false;
            this.Label.Alignment = GoObject.Middle;
            this.Label.Multiline = false;
            this.Label.Wrapping = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="env"></param>
        /// <returns></returns>
        public override GoObject CopyObject(GoCopyDictionary env)
        {
            RuleNode newobj = (RuleNode)base.CopyObject(env);
            if (this.Info != null)
            {
                // copy Info, and make sure the new Info's parent Node reference is updated
                newobj.Info = (Info)this.Info.Clone();
                newobj.Info.Node = newobj;
            }
            return newobj;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="childchanged"></param>
        public override void LayoutChildren(GoObject childchanged)
        {
            base.LayoutChildren(childchanged);
            GoImage img = this.Image;
            GoText lab = this.Label;
            if (img != null && lab != null)
            {
                PointF p = lab.GetSpotLocation(MiddleLeft);
                p.X -= 4;  // space between image and label
                img.SetSpotLocation(MiddleRight, p);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public RuleNodeType Kind
        {
            get { return myKind; }
            set { myKind = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="kind"></param>
        public virtual void ChangeNodeForKind(RuleNodeType kind)
        {
            switch (kind)
            {
                case RuleNodeType.Start:
                    {
                        //this.Selectable = false;
                        this.Deletable = false;
                        this.AutoResizes = false;
                        GoShape s = new GoEllipse();
                        s.Size = new SizeF(40, 40);
                        s.Brush = Brushes.DarkGreen;
                        this.Background = s;
                        this.Label.Bold = true;
                        this.Label.TextColor = Color.White;
                        this.TopPort = null;
                        this.LeftPort = null;
                        this.RightPort = null;
                        this.Info = null;
                        break;
                    }
                case RuleNodeType.End:
                    {
                        //this.Selectable = false;
                        this.Deletable = false;
                        this.AutoResizes = false;
                        GoOctagon s = new GoOctagon();
                        s.Reshapable = false;
                        s.Brush = Brushes.Red;
                        s.Size = new SizeF(40, 40);
                        this.Background = s;
                        this.Label.Bold = true;
                        this.Label.TextColor = Color.White;
                        this.LeftPort = null;
                        this.RightPort = null;
                        this.BottomPort = null;
                        this.Info = null;
                        break;
                    }
                case RuleNodeType.Conditional:
                    {
                        GoShape s = new GoDiamond();
                        s.Brush = Brushes.LightCyan;
                        s.Size = new SizeF(120, 40);  // but pointless when AutoResizes is true
                        this.Background = s;
                        this.TopLeftMargin = new SizeF(30, 10);
                        this.BottomRightMargin = new SizeF(30, 10);
                        this.Label.Editable = true;
                        this.Label.BackgroundColor = Color.White;
                        this.BottomPort = null;
                        break;
                    }
                case RuleNodeType.Action:
                    {
                        this.Brush = Brushes.Yellow;
                        this.Background.Size = new SizeF(100, 40);  // but pointless when AutoResizes is true
                        this.Label.Editable = true;
                        this.Label.BackgroundColor = Color.White;
                        this.LeftPort = null;
                        this.RightPort = null;
                        break;
                    }
                case RuleNodeType.Effect:
                    {
                        this.Brush = Brushes.LightBlue;
                        this.Background.Size = new SizeF(100, 40);  // but pointless when AutoResizes is true
                        this.Label.Editable = true;
                        this.Label.BackgroundColor = Color.White;
                        this.LeftPort = null;
                        this.RightPort = null;
                        break;
                    }
                case RuleNodeType.Output:
                    {
                        GoRoundedRectangle s = new GoRoundedRectangle();
                        s.Corner = new SizeF(15, 15);
                        s.Brush = Brushes.Pink;
                        s.Size = new SizeF(100, 40);  // but pointless when AutoResizes is true
                        this.Background = s;
                        this.Label.Editable = true;
                        this.Label.BackgroundColor = Color.White;
                        this.LeftPort = null;
                        this.RightPort = null;
                        this.BottomPort = null;
                        break;
                    }
                default:
                    Debug.Fail("unknown node kind: " + kind.ToString());
                    break;
            }
            // leave a little more room for any arrows of links coming into a node
            if (this.TopPort != null)
            {
                this.TopPort.EndSegmentLength = 15;
            }

            // add an icon for certains kinds of nodes
            switch (kind)
            {
                case RuleNodeType.Conditional:
                case RuleNodeType.Action:
                case RuleNodeType.Effect:
                case RuleNodeType.Output:
                    {
                        SizeF margins = this.TopLeftMargin;
                        this.TopLeftMargin = new SizeF(margins.Width + 4 + StandardImageSize.Width, margins.Height);
                        
                        // add the GoImage
                        GoImage img = new GoImage();
                        img.Selectable = false;
                        img.Size = GoObject.LargestSizeKeepingAspectRatio(StandardImageSize, img.Size);
                        Add(img);
                        // make room for the Icon
                        
                        break;
                    }
                default: break;
            }
        }

        public readonly SizeF StandardImageSize = new SizeF(20, 20);

        /// <summary>
        /// find the image acting as an icon, if any
        /// </summary>
        public GoImage Image
        {
            get
            {
                foreach (GoObject obj in this)
                {
                    GoImage img = obj as GoImage;
                    if (img != null) return img;
                }
                return null;
                //GoImage goImg = new GoImage();
                //goImg.Selectable = false;
                //PointF p = this.GetSpotLocation(MiddleLeft);
                //p.X -= 4;  // space between image and label
                //goImg.SetSpotLocation(MiddleRight, p);
                //this.Add(goImg);
                //return goImg;
            }
            set
            {
                bool foundImage = false;
                foreach (GoObject obj in this)
                {
                    GoImage img = obj as GoImage;
                    if (img != null)
                    {
                        img = value;
                        foundImage = true;
                    }
                }
                if (!foundImage)
                {
                    value.Selectable = false;
                    value.Size = GoObject.LargestSizeKeepingAspectRatio(StandardImageSize, value.Size);
                    Add(value);
                }
            }
        }

        /// <summary>
        /// supply an Info to hold node-kind-specific information
        /// </summary>
        /// <param name="kind"></param>
        public virtual void ChangeInfoForKind(RuleNodeType kind)
        {
            switch (kind)
            {
                case RuleNodeType.Start: this.Info = null; break;
                case RuleNodeType.End: this.Info = null; break;
                case RuleNodeType.Conditional: this.Info = new ConditionalInfo(this); break;
                case RuleNodeType.Action: this.Info = new ActionInfo(this); break;
                case RuleNodeType.Effect: this.Info = new EffectInfo(this); break;
                case RuleNodeType.Output: this.Info = new OutputInfo(this); break;
                default:
                    Debug.Fail("unknown node kind: " + kind.ToString());
                    break;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Info Info
        {
            get { return this.UserObject as Info; }
            set { this.UserObject = value; }
        }

        /// <summary>
        /// nodes implement IHighlit, so that dragging in the view can show which node it's over
        /// </summary>
        public bool Highlit
        {
            get { return myHighlit; }
            set
            {
                bool old = myHighlit;
                if (old != value)
                {
                    myHighlit = value;
                    this.SkipsUndoManager = true;
                    if (value)
                    {
                        this.Pen = new Pen(Color.Red, 5);
                    }
                    else
                    {
                        this.Pen = Pens.Black;
                    }
                    this.SkipsUndoManager = false;
                }
            }
        }

        /// <summary>
        /// show additional information about a node, without having to select it
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        public override String GetToolTip(GoView view)
        {
            if(this.UserObject !=null && this.UserObject.GetType().IsSubclassOf(typeof(RuleNodeDataBase)))
            {
                RuleNodeDataBase ruleNodeData = (RuleNodeDataBase) this.UserObject;
                return ruleNodeData.Description;
            }
            String msg = "";
            switch (this.Kind)
            {
                case RuleNodeType.Start: msg = "a Start"; break;
                case RuleNodeType.End: msg = "an End"; break;
                case RuleNodeType.Conditional: msg = "an If"; break;
                case RuleNodeType.Action: msg = "an Action"; break;
                case RuleNodeType.Effect: msg = "an Effect"; break;
                case RuleNodeType.Output: msg = "an Output"; break;
                default:
                    Debug.Fail("unknown node kind: " + this.Kind.ToString());
                    break;
            }
            Info info = this.Info;
            if (info != null)
            {
                msg += Environment.NewLine + info.ToString();
            }
            return msg;
        }

        private RuleNodeType myKind = RuleNodeType.Action;
        private bool myHighlit = false;
    }
}
